package com.mycompany.crm.aspect;

import com.alibaba.fastjson.JSON;
import com.mycompany.crm.annotation.LogAnno;
import com.mycompany.crm.dao.LogMapper;
import com.mycompany.crm.dao.UserMapper;
import com.mycompany.crm.utils.LoginUserUtil;
import com.mycompany.crm.utils.ManagerThreadPool;
import com.mycompany.crm.vo.Log;
import com.mycompany.crm.vo.User;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * @author jy
 * @version 1.0
 */
@Component
@Aspect
public class LogAspect {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private LogMapper logMapper;

    /**
     * 日志记录的环绕通知
     * 其实 日志可以直接放在es里面去
     *
     * @param joinPoint
     * @return
     */
    @Around(value = "@annotation(com.mycompany.crm.annotation.LogAnno)")
    public Object logAround(ProceedingJoinPoint joinPoint) {
        Object result = null;
        // 方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 拿到方法的名字
        String methodName = method.getName();
        // 注解里面的属性
        String description = "";
        if (method.isAnnotationPresent(LogAnno.class)) {
            LogAnno egoLog = method.getAnnotation(LogAnno.class);
            description = egoLog.description();
        }
        // 参数
        String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
        Object[] args = joinPoint.getArgs();
        Object[] arguments = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof HttpServletRequest || args[i] instanceof HttpServletResponse || args[i] instanceof MultipartFile) {
                //ServletRequest不能序列化，从入参里排除，否则报异常：java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
                //ServletResponse不能序列化 从入参里排除，否则报异常：java.lang.IllegalStateException: getOutputStream() has already been called for this response
                continue;
            }
            arguments[i] = argNames[i] + ":" +args[i];
        }
        String arg = JSON.toJSONString(arguments);
        // 拿ip
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert requestAttributes != null;
        HttpServletRequest request = requestAttributes.getRequest();
        String ip = request.getRemoteAddr();
        long start = System.currentTimeMillis();
        // 执行目标方法
        try {
            result = joinPoint.proceed(args);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        long end = System.currentTimeMillis();
        // 拿到执行时间
        long time = end - start;
        // 拿到用户名称
        Integer userId = LoginUserUtil.releaseUserIdFromCookie(request);
        // 异步处理
        String finalOperation = description;
        ManagerThreadPool.poolExecutor.execute(() -> {
            User user = userMapper.selectByPrimaryKey(userId);
            // 组装sysLog对象
            Log log = Log.builder()
                    .createMan(user.getUserName())
                    .description(finalOperation)
                    .method(methodName)
                    .params(arg)
                    .executeTime((int) time)
                    .requestIp(ip)
                    .createDate(new Date())
                    .type("1")
                    .exceptionCode("200")
                    .exceptionDetail("操作成功")
                    .build();
            // 插入数据库
            logMapper.insert(log);
        });
        return result;
    }

}
